// Free Disassembler and Assembler -- Assembler
//
// Copyright (C) 2001 Oleh Yuschuk
//
//  This program is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation; either version 2 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

// 16.01.2002 - corrected error in processing of immediate constants.


#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
#include <float.h>
#pragma hdrstop
#pragma warning(disable:4996)
#pragma warning(disable:4309)

#include "disasm.h"

////////////////////////////////////////////////////////////////////////////////
///////////////////////////// ASSEMBLER FUNCTIONS //////////////////////////////

// Scanner modes.
#define SA_NAME        0x0001          // Don't try to decode labels
#define SA_IMPORT      0x0002          // Allow import pseudolabel

// Types of input tokens reported by scanner.
#define SCAN_EOL       0               // End of line
#define SCAN_REG8      1               // 8-bit register
#define SCAN_REG16     2               // 16-bit register
#define SCAN_REG32     3               // 32-bit register
#define SCAN_SEG       4               // Segment register
#define SCAN_FPU       5               // FPU register
#define SCAN_MMX       6               // MMX register
#define SCAN_CR        7               // Control register
#define SCAN_DR        8               // Debug register
#define SCAN_OPSIZE    9               // Operand size modifier
#define SCAN_JMPSIZE   10              // Jump size modifier
#define SCAN_LOCAL     11              // Address on stack in form LOCAL.decimal
#define SCAN_ARG       12              // Address on stack in form ARG.decimal
#define SCAN_PTR       20              // PTR in MASM addressing statements
#define SCAN_REP       21              // REP prefix
#define SCAN_REPE      22              // REPE prefix
#define SCAN_REPNE     23              // REPNE prefix
#define SCAN_LOCK      24              // LOCK prefix
#define SCAN_NAME      25              // Command or label
#define SCAN_ICONST    26              // Hexadecimal constant
#define SCAN_DCONST    27              // Decimal constant
#define SCAN_OFS       28              // Undefined constant
#define SCAN_FCONST    29              // Floating-point constant
#define SCAN_EIP       30              // Register EIP
#define SCAN_SIGNED    31              // Keyword "SIGNED" (in expressions)
#define SCAN_UNSIGNED  32              // Keyword "UNSIGNED" (in expressions)
#define SCAN_CHAR      33              // Keyword "CHAR" (in expressions)
#define SCAN_FLOAT     34              // Keyword "FLOAT" (in expressions)
#define SCAN_DOUBLE    35              // Keyword "DOUBLE" (in expressions)
#define SCAN_FLOAT10   36              // Keyword "FLOAT10" (in expressions)
#define SCAN_STRING    37              // Keyword "STRING" (in expressions)
#define SCAN_UNICODE   38              // Keyword "UNICODE" (in expressions)
#define SCAN_MSG       39              // Pseudovariable MSG (in expressions)

#define SCAN_SYMB      64              // Any other character
#define SCAN_IMPORT    65              // Import pseudolabel
#define SCAN_ERR       255             // Definitely bad item

// Definition used by Assembler to report command matching errors.
#define MA_JMP         0x0001          // Invalid jump size modifier
#define MA_NOP         0x0002          // Wrong number of operands
#define MA_TYP         0x0004          // Bad operand type
#define MA_NOS         0x0008          // Explicit operand size expected
#define MA_SIZ         0x0010          // Bad operand size
#define MA_DIF         0x0020          // Different operand sizes
#define MA_SEG         0x0040          // Invalid segment register
#define MA_RNG         0x0080          // Constant out of expected range

typedef struct t_asmoperand
{
    int            type;                 // Operand type, see beginning of file
    int            size;                 // Operand size or 0 if yet unknown
    int            index;                // Index or other register
    int            scale;                // Scale
    int            base;                 // Base register if present
    long           offset;               // Immediate value or offset
    int            anyoffset;            // Offset is present but undefined
    int            segment;              // Segment in address if present
    int            jmpmode;              // Specified jump size
} t_asmoperand;

static char      *asmcmd;              // Pointer to 0-terminated source line
static int       scan;                 // Type of last scanned element
static int       prio;                 // Priority of operation (0: highest)
static char      sdata[TEXTLEN];       // Last scanned name (depends on type)
static long      idata;                // Last scanned value
static long      double fdata;         // Floating-point number
static char      *asmerror;            // Explanation of last error, or NULL

// Simple and slightly recursive scanner shared by Assemble(). The scanner is
// straightforward and ineffective, but high speed is not a must here. As
// input, it uses global pointer to source line asmcmd. On exit, it fills in
// global variables scan, prio, sdata, idata and/or fdata. If some error is
// detected, asmerror points to error message, otherwise asmerror remains
// unchanged.
static void Scanasm( int mode )
{
    int i, j, base, maxdigit;
    long decimal, hex;
    long double floating, divisor;
    char s[TEXTLEN], *pcmd;
    sdata[0] = '\0';
    idata = 0;

    if ( asmcmd == NULL )
    {
        asmerror = "NULL input line";
        scan = SCAN_ERR;
        return;
    };

    while ( *asmcmd == ' ' || *asmcmd == '\t' )
        asmcmd++;                          // Skip leading spaces

    if ( *asmcmd == '\0' || *asmcmd == ';' )
    {
        scan = SCAN_EOL;
        return;
    };          // Empty line

    if ( isalpha( *asmcmd ) || *asmcmd == '_' || *asmcmd == '@' )
    {
        sdata[0] = *asmcmd++;
        i = 1;         // Some keyword or identifier

        while ( ( isalnum( *asmcmd ) || *asmcmd == '_' || *asmcmd == '@' ) &&
                i < sizeof( sdata ) )
            sdata[i++] = *asmcmd++;

        if ( i >= sizeof( sdata ) )
        {
            asmerror = "Too long identifier";
            scan = SCAN_ERR;
            return;
        };

        sdata[i] = '\0';

        while ( *asmcmd == ' ' || *asmcmd == '\t' )
            asmcmd++;                        // Skip trailing spaces

        strcpy( s, sdata );
        strupr( s );

        for ( j = 0; j <= 8; j++ )         // j==8 means "any register"
        {
            if ( strcmp( s, regname[0][j] ) != 0 ) continue;

            idata = j;
            scan = SCAN_REG8;       // 8-bit register
            return;
        };

        for ( j = 0; j <= 8; j++ )
        {
            if ( strcmp( s, regname[1][j] ) != 0 ) continue;

            idata = j;
            scan = SCAN_REG16;      // 16-bit register
            return;
        };

        for ( j = 0; j <= 8; j++ )
        {
            if ( strcmp( s, regname[2][j] ) != 0 ) continue;

            idata = j;
            scan = SCAN_REG32;      // 32-bit register
            return;
        };

        for ( j = 0; j < 6; j++ )
        {
            if ( strcmp( s, segname[j] ) != 0 ) continue;

            idata = j;
            scan = SCAN_SEG;        // Segment register

            while ( *asmcmd == ' ' || *asmcmd == '\t' )
                asmcmd++;                      // Skip trailing spaces

            return;
        };

        if ( strcmp( s, "ST" ) == 0 )
        {
            pcmd = asmcmd;
            Scanasm( SA_NAME ); // FPU register

            if ( scan != SCAN_SYMB || idata != '(' )
            {
                asmcmd = pcmd;                 // Undo last scan
                idata = 0;
                scan = SCAN_FPU;
                return;
            };

            Scanasm( SA_NAME );

            j = idata;

            if ( ( scan != SCAN_ICONST && scan != SCAN_DCONST ) || idata < 0 || idata > 7 )
            {
                asmerror = "FPU registers have indexes 0 to 7";
                scan = SCAN_ERR;
                return;
            };

            Scanasm( SA_NAME );

            if ( scan != SCAN_SYMB || idata != ')' )
            {
                asmerror = "Closing parenthesis expected";
                scan = SCAN_ERR;
                return;
            };

            idata = j;

            scan = SCAN_FPU;

            return;
        };

        for ( j = 0; j <= 8; j++ )
        {
            if ( strcmp( s, fpuname[j] ) != 0 ) continue;

            idata = j;
            scan = SCAN_FPU;        // FPU register (alternative coding)
            return;
        };

        for ( j = 0; j <= 8; j++ )
        {
            if ( strcmp( s, mmxname[j] ) != 0 ) continue;

            idata = j;
            scan = SCAN_MMX;        // MMX register
            return;
        };

        for ( j = 0; j <= 8; j++ )
        {
            if ( strcmp( s, crname[j] ) != 0 ) continue;

            idata = j;
            scan = SCAN_CR;         // Control register
            return;
        };

        for ( j = 0; j <= 8; j++ )
        {
            if ( strcmp( s, drname[j] ) != 0 ) continue;

            idata = j;
            scan = SCAN_DR;         // Debug register
            return;
        };

        for ( j = 0; j < sizeof( sizename ) / sizeof( sizename[0] ); j++ )
        {
            if ( strcmp( s, sizename[j] ) != 0 ) continue;

            pcmd = asmcmd;
            Scanasm( SA_NAME );

            if ( scan != SCAN_PTR )          // Fetch non-functional "PTR"
                asmcmd = pcmd;

            idata = j;
            scan = SCAN_OPSIZE;     // Operand (data) size in bytes
            return;
        };

        if ( strcmp( s, "EIP" ) == 0 )     // Register EIP
        {
            scan = SCAN_EIP;
            idata = 0;
            return;
        };

        if ( strcmp( s, "SHORT" ) == 0 )   // Relative jump has 1-byte offset
        {
            scan = SCAN_JMPSIZE;
            idata = 1;
            return;
        };

        if ( strcmp( s, "LONG" ) == 0 )    // Relative jump has 4-byte offset
        {
            scan = SCAN_JMPSIZE;
            idata = 2;
            return;
        };

        if ( strcmp( s, "NEAR" ) == 0 )    // Jump within same code segment
        {
            scan = SCAN_JMPSIZE;
            idata = 4;
            return;
        };

        if ( strcmp( s, "FAR" ) == 0 )     // Jump to different code segment
        {
            scan = SCAN_JMPSIZE;
            idata = 8;
            return;
        };

        if ( strcmp( s, "LOCAL" ) == 0 && *asmcmd == '.' )
        {
            asmcmd++;

            while ( *asmcmd == ' ' || *asmcmd == '\t' )
                asmcmd++;                      // Skip trailing spaces

            if ( !isdigit( *asmcmd ) )
            {
                asmerror = "Integer number expected";
                scan = SCAN_ERR;
                return;
            };

            while ( isdigit( *asmcmd ) )     // LOCAL index is decimal number!
                idata = idata * 10 + ( *asmcmd++ ) - '0';

            scan = SCAN_LOCAL;
            return;
        };

        if ( strcmp( s, "ARG" ) == 0 && *asmcmd == '.' )
        {
            asmcmd++;

            while ( *asmcmd == ' ' || *asmcmd == '\t' )
                asmcmd++;                      // Skip trailing spaces

            if ( !isdigit( *asmcmd ) )
            {
                asmerror = "Integer number expected";
                scan = SCAN_ERR;
                return;
            };

            while ( isdigit( *asmcmd ) )     // ARG index is decimal number!
                idata = idata * 10 + ( *asmcmd++ ) - '0';

            scan = SCAN_ARG;
            return;
        };

        if ( strcmp( s, "REP" ) == 0 )
        {
            scan = SCAN_REP;
            return;
        };        // REP prefix

        if ( strcmp( s, "REPE" ) == 0 || strcmp( s, "REPZ" ) == 0 )
        {
            scan = SCAN_REPE;
            return;
        };       // REPE prefix

        if ( strcmp( s, "REPNE" ) == 0 || strcmp( s, "REPNZ" ) == 0 )
        {
            scan = SCAN_REPNE;
            return;
        };      // REPNE prefix

        if ( strcmp( s, "LOCK" ) == 0 )
        {
            scan = SCAN_LOCK;
            return;
        };       // LOCK prefix

        if ( strcmp( s, "PTR" ) == 0 )
        {
            scan = SCAN_PTR;
            return;
        };        // PTR in MASM addressing statements

        if ( strcmp( s, "CONST" ) == 0 || strcmp( s, "OFFSET" ) == 0 )
        {
            scan = SCAN_OFS;
            return;
        };        // Present but undefined offset/constant

        if ( strcmp( s, "SIGNED" ) == 0 )
        {
            scan = SCAN_SIGNED;
            return;
        };     // Keyword "SIGNED" (in expressions)

        if ( strcmp( s, "UNSIGNED" ) == 0 )
        {
            scan = SCAN_UNSIGNED;
            return;
        };   // Keyword "UNSIGNED" (in expressions)

        if ( strcmp( s, "CHAR" ) == 0 )
        {
            scan = SCAN_CHAR;
            return;
        };       // Keyword "CHAR" (in expressions)

        if ( strcmp( s, "FLOAT" ) == 0 )
        {
            scan = SCAN_FLOAT;
            return;
        };      // Keyword "FLOAT" (in expressions)

        if ( strcmp( s, "DOUBLE" ) == 0 )
        {
            scan = SCAN_DOUBLE;
            return;
        };     // Keyword "DOUBLE" (in expressions)

        if ( strcmp( s, "FLOAT10" ) == 0 )
        {
            scan = SCAN_FLOAT10;
            return;
        };    // Keyword "FLOAT10" (in expressions)

        if ( strcmp( s, "STRING" ) == 0 )
        {
            scan = SCAN_STRING;
            return;
        };     // Keyword "STRING" (in expressions)

        if ( strcmp( s, "UNICODE" ) == 0 )
        {
            scan = SCAN_UNICODE;
            return;
        };    // Keyword "UNICODE" (in expressions)

        if ( strcmp( s, "MSG" ) == 0 )
        {
            scan = SCAN_MSG;
            return;
        };        // Pseudovariable MSG (in expressions)

        if ( mode & SA_NAME )
        {
            idata = i;
            scan = SCAN_NAME;       // Don't try to decode symbolic label
            return;
        }

        asmerror = "Unknown identifier";
        scan = SCAN_ERR;
        return;
    }
    else if ( isdigit( *asmcmd ) )       // Constant
    {
        base = 0;
        maxdigit = 0;
        decimal = hex = 0L;
        floating = 0.0;

        if ( asmcmd[0] == '0' && toupper( asmcmd[1] ) == 'X' )
        {
            base = 16;
            asmcmd += 2;
        };           // Force hexadecimal number

        while ( 1 )
        {
            if ( isdigit( *asmcmd ) )
            {
                decimal = decimal * 10 + ( *asmcmd ) - '0';
                floating = floating * 10.0 + ( *asmcmd ) - '0';
                hex = hex * 16 + ( *asmcmd ) - '0';

                if ( maxdigit == 0 ) maxdigit = 9;

                asmcmd++;
            }
            else if ( isxdigit( *asmcmd ) )
            {
                hex = hex * 16 + toupper( *asmcmd++ ) - 'A' + 10;
                maxdigit = 15;
            }
            else break;
        };

        if ( maxdigit == 0 )
        {
            asmerror = "Hexadecimal digits after 0x... expected";
            scan = SCAN_ERR;
            return;
        };

        if ( toupper( *asmcmd ) == 'H' )   // Force hexadecimal number
        {
            if ( base == 16 )
            {
                asmerror = "Please don't mix 0xXXXX and XXXXh forms";
                scan = SCAN_ERR;
                return;
            };

            asmcmd++;

            idata = hex;

            scan = SCAN_ICONST;

            while ( *asmcmd == ' ' || *asmcmd == '\t' ) asmcmd++;

            return;
        };

        if ( *asmcmd == '.' )              // Force decimal number
        {
            if ( base == 16 || maxdigit > 9 )
            {
                asmerror = "Not a decimal number";
                scan = SCAN_ERR;
                return;
            };

            asmcmd++;

            if ( isdigit( *asmcmd ) || toupper( *asmcmd ) == 'E' )
            {
                divisor = 1.0;

                while ( isdigit( *asmcmd ) )   // Floating-point number
                {
                    divisor /= 10.0;
                    floating += divisor * ( *asmcmd - '0' );
                    asmcmd++;
                };

                if ( toupper( *asmcmd ) == 'E' )
                {
                    asmcmd++;

                    if ( *asmcmd == '-' )
                    {
                        base = -1;
                        asmcmd++;
                    }
                    else base = 1;

                    if ( !isdigit( *asmcmd ) )
                    {
                        asmerror = "Invalid exponent";
                        scan = SCAN_ERR;
                        return;
                    };

                    decimal = 0;

                    while ( isdigit( *asmcmd ) )
                    {
                        if ( decimal < 65536L ) decimal = decimal * 10 + ( *asmcmd++ ) - '0';
                    };

                    floating *= pow( 10.0, decimal * base );
                };

                fdata = floating;

                scan = SCAN_FCONST;

                return;
            }
            else
            {
                idata = decimal;
                scan = SCAN_DCONST;

                while ( *asmcmd == ' ' || *asmcmd == '\t' ) asmcmd++;

                return;
            };
        };

        idata = hex;

        scan = SCAN_ICONST;     // Default is hexadecimal

        while ( *asmcmd == ' ' || *asmcmd == '\t' ) asmcmd++;

        return;
    }
    else if ( *asmcmd == '\'' )          // Character constant
    {
        asmcmd++;

        if ( *asmcmd == '\0' || ( *asmcmd == '\\' && asmcmd[1] == '\0' ) )
        {
            asmerror = "Unterminated character constant";
            scan = SCAN_ERR;
            return;
        };

        if ( *asmcmd == '\'' )
        {
            asmerror = "Empty character constant";
            scan = SCAN_ERR;
            return;
        };

        if ( *asmcmd == '\\' ) asmcmd++;

        idata = *asmcmd++;

        if ( *asmcmd != '\'' )
        {
            asmerror = "Unterminated character constant";
            scan = SCAN_ERR;
            return;
        };

        asmcmd++;

        while ( *asmcmd == ' ' || *asmcmd == '\t' ) asmcmd++;

        scan = SCAN_ICONST;
        return;
    }
    else                                 // Any other character or combination
    {
        idata = sdata[0] = *asmcmd++;
        sdata[1] = sdata[2] = '\0';

        if ( idata == '|' && *asmcmd == '|' )
        {
            idata = '||';
            prio = 10;           // '||'
            sdata[1] = *asmcmd++;
        }
        else if ( idata == '&' && *asmcmd == '&' )
        {
            idata = '&&';
            prio = 9;            // '&&'
            sdata[1] = *asmcmd++;
        }
        else if ( idata == '=' && *asmcmd == '=' )
        {
            idata = '==';
            prio = 5;            // '=='
            sdata[1] = *asmcmd++;
        }
        else if ( idata == '!' && *asmcmd == '=' )
        {
            idata = '!=';
            prio = 5;            // '!='
            sdata[1] = *asmcmd++;
        }
        else if ( idata == '<' && *asmcmd == '=' )
        {
            idata = '<=';
            prio = 4;            // '<='
            sdata[1] = *asmcmd++;
        }
        else if ( idata == '>' && *asmcmd == '=' )
        {
            idata = '>=';
            prio = 4;            // '>='
            sdata[1] = *asmcmd++;
        }
        else if ( idata == '<' && *asmcmd == '<' )
        {
            idata = '<<';
            prio = 3;            // '<<'
            sdata[1] = *asmcmd++;
        }
        else if ( idata == '>' && *asmcmd == '>' )
        {
            idata = '>>';
            prio = 3;            // '>>'
            sdata[1] = *asmcmd++;
        }
        else if ( idata == '|' ) prio = 8; // '|'
        else if ( idata == '^' ) prio = 7; // '^'
        else if ( idata == '&' ) prio = 6; // '&'
        else if ( idata == '<' )
        {
            if ( *asmcmd == '&' )            // Import pseudolabel (for internal use)
            {
                if ( ( mode & SA_IMPORT ) == 0 )
                {
                    asmerror = "Syntax error";
                    scan = SCAN_ERR;
                    return;
                };

                asmcmd++;

                i = 0;

                while ( *asmcmd != '\0' && *asmcmd != '>' )
                {
                    sdata[i++] = *asmcmd++;

                    if ( i >= sizeof( sdata ) )
                    {
                        asmerror = "Too long import name";
                        scan = SCAN_ERR;
                        return;
                    };
                };

                if ( *asmcmd != '>' )
                {
                    asmerror = "Unterminated import name";
                    scan = SCAN_ERR;
                    return;
                };

                asmcmd++;

                sdata[i] = '\0';

                scan = SCAN_IMPORT;

                return;
            }
            else prio = 4;
        }                   // '<'
        else if ( idata == '>' ) prio = 4; // '>'
        else if ( idata == '+' ) prio = 2; // '+'
        else if ( idata == '-' ) prio = 2; // '-'
        else if ( idata == '*' ) prio = 1; // '*'
        else if ( idata == '/' ) prio = 1; // '/'
        else if ( idata == '%' ) prio = 1; // '%'
        else if ( idata == ']' )
        {
            pcmd = asmcmd;
            Scanasm( SA_NAME );

            if ( scan != SCAN_SYMB || idata != '[' )
            {
                idata = ']';
                asmcmd = pcmd;
                prio = 0;
            }
            else
            {
                idata = '+';
                prio = 2;           // Translate '][' to '+'
            };
        }
        else prio = 0;                     // Any other character

        scan = SCAN_SYMB;
        return;
    };
};

// Fetches one complete operand from the input line and fills in structure op
// with operand's data. Expects that first token of the operand is already
// scanned. Supports operands in generalized form (for example, R32 means any
// of general-purpose 32-bit integer registers).
static void Parseasmoperand( t_asmoperand *op )
{
    int i, j, bracket, sign, xlataddr;
    int reg, r[9];
    long offset;

    if ( scan == SCAN_EOL || scan == SCAN_ERR )
        return;                            // No or bad operand

    // Jump or call address may begin with address size modifier(s) SHORT, LONG,
    // NEAR and/or FAR. Not all combinations are allowed. After operand is
    // completely parsed, this function roughly checks whether modifier is
    // allowed. Exact check is done in Assemble().
    if ( scan == SCAN_JMPSIZE )
    {
        j = 0;

        while ( scan == SCAN_JMPSIZE )
        {
            j |= idata;                      // Fetch all size modifiers
            Scanasm( 0 );
        };

        if (
            ( ( j & 0x03 ) == 0x03 ) ||      // Mixed SHORT and LONG
            ( ( j & 0x0C ) == 0x0C ) ||      // Mixed NEAR and FAR
            ( ( j & 0x09 ) == 0x09 )         // Mixed FAR and SHORT
        )
        {
            asmerror = "Invalid combination of jump address modifiers";
            scan = SCAN_ERR;
            return;
        };

        if ( ( j & 0x08 ) == 0 ) j |= 0x04; // Force NEAR if not FAR

        op->jmpmode = j;
    };

    // Simple operands are either register or constant, their processing is
    // obvious and straightforward.
    if ( scan == SCAN_REG8 || scan == SCAN_REG16 || scan == SCAN_REG32 )
    {
        op->type = REG;
        op->index = idata;   // Integer general-purpose register

        if ( scan == SCAN_REG8 ) op->size = 1;
        else if ( scan == SCAN_REG16 ) op->size = 2;
        else op->size = 4;
    }
    else if ( scan == SCAN_FPU )         // FPU register
    {
        op->type = RST;
        op->index = idata;
    }
    else if ( scan == SCAN_MMX )         // MMX or 3DNow! register
    {
        op->type = RMX;
        op->index = idata;
    }
    else if ( scan == SCAN_CR )          // Control register
    {
        op->type = CRX;
        op->index = idata;
    }
    else if ( scan == SCAN_DR )          // Debug register
    {
        op->type = DRX;
        op->index = idata;
    }
    else if ( scan == SCAN_SYMB && idata == '-' )
    {
        Scanasm( 0 );                      // Negative constant

        if ( scan != SCAN_ICONST && scan != SCAN_DCONST && scan != SCAN_OFS )
        {
            asmerror = "Integer number expected";
            scan = SCAN_ERR;
            return;
        };

        op->type = IMM;

        op->offset = -idata;

        if ( scan == SCAN_OFS ) op->anyoffset = 1;
    }
    else if ( scan == SCAN_SYMB && idata == '+' )
    {
        Scanasm( 0 );                      // Positive constant

        if ( scan != SCAN_ICONST && scan != SCAN_DCONST && scan != SCAN_OFS )
        {
            asmerror = "Integer number expected";
            scan = SCAN_ERR;
            return;
        };

        op->type = IMM;

        op->offset = idata;

        if ( scan == SCAN_OFS ) op->anyoffset = 1;
    }
    else if ( scan == SCAN_ICONST || scan == SCAN_DCONST || scan == SCAN_OFS )
    {
        j = idata;

        if ( scan == SCAN_OFS ) op->anyoffset = 1;

        Scanasm( 0 );

        if ( scan == SCAN_SYMB && idata == ':' )
        {
            Scanasm( 0 );                    // Absolute long address (seg:offset)

            if ( scan != SCAN_ICONST && scan != SCAN_DCONST && scan != SCAN_OFS )
            {
                asmerror = "Integer address expected";
                scan = SCAN_ERR;
                return;
            };

            op->type = JMF;

            op->offset = idata;

            op->segment = j;

            if ( scan == SCAN_OFS ) op->anyoffset = 1;
        }
        else
        {
            op->type = IMM;
            op->offset = j;    // Constant without sign
            return;                          // Next token already scanned
        };
    }
    else if ( scan == SCAN_FCONST )
    {
        asmerror = "Floating-point numbers are not allowed in command";
        scan = SCAN_ERR;
        return;
    }
    // Segment register or address.
    else if ( scan == SCAN_SEG || scan == SCAN_OPSIZE ||
              ( scan == SCAN_SYMB && idata == '[' )
            )                                    // Segment register or address
    {
        bracket = 0;

        if ( scan == SCAN_SEG )
        {
            j = idata;
            Scanasm( 0 );

            if ( scan != SCAN_SYMB || idata != ':' )
            {
                op->type = SGM;
                op->index = j;   // Segment register as operand
                return;
            };                     // Next token already scanned

            op->segment = j;

            Scanasm( 0 );
        };

        // Scan 32-bit address. This parser does not support 16-bit addresses.
        // First of all, get size of operand (optional), segment register (optional)
        // and opening bracket (required).
        while ( 1 )
        {
            if ( scan == SCAN_SYMB && idata == '[' )
            {
                if ( bracket )                 // Bracket
                {
                    asmerror = "Only one opening bracket allowed";
                    scan = SCAN_ERR;
                    return;
                };

                bracket = 1;
            }
            else if ( scan == SCAN_OPSIZE )
            {
                if ( op->size != 0 )           // Size of operand
                {
                    asmerror = "Duplicated size modifier";
                    scan = SCAN_ERR;
                    return;
                };

                op->size = idata;
            }
            else if ( scan == SCAN_SEG )
            {
                if ( op->segment != SEG_UNDEF ) // Segment register
                {
                    asmerror = "Duplicated segment register";
                    scan = SCAN_ERR;
                    return;
                };

                op->segment = idata;

                Scanasm( 0 );

                if ( scan != SCAN_SYMB || idata != ':' )
                {
                    asmerror = "Semicolon expected";
                    scan = SCAN_ERR;
                    return;
                };
            }
            else if ( scan == SCAN_ERR )
                return;
            else break;                      // None of expected address elements

            Scanasm( 0 );
        };

        if ( bracket == 0 )
        {
            asmerror = "Address expression requires brackets";
            scan = SCAN_ERR;
            return;
        };

        // Assembling a 32-bit address may be a kind of nigthmare, due to a large
        // number of allowed forms. Parser collects immediate offset in op->offset
        // and count for each register in array r[]. Then it decides whether this
        // combination is valid and determines scale, index and base. Assemble()
        // will use these numbers to select address form (with or without SIB byte,
        // 8- or 32-bit offset, use segment prefix or not). As a useful side effect
        // of this technique, one may specify, for example, [EAX*5] which will
        // correctly assemble to [EAX*4+EAX].
        for ( i = 0; i <= 8; i++ ) r[i] = 0;

        sign = '+';                        // Default sign for the first operand
        xlataddr = 0;

        while ( 1 )                        // Get SIB and offset
        {
            if ( scan == SCAN_SYMB && ( idata == '+' || idata == '-' ) )
            {
                sign = idata;
                Scanasm( 0 );
            };

            if ( scan == SCAN_ERR ) return;

            if ( sign == '?' )
            {
                asmerror = "Syntax error";
                scan = SCAN_ERR;
                return;
            };

            // Register AL appears as part of operand of (seldom used) command XLAT.
            if ( scan == SCAN_REG8 && idata == REG_EAX )
            {
                if ( sign == '-' )
                {
                    asmerror = "Unable to subtract register";
                    scan = SCAN_ERR;
                    return;
                };

                if ( xlataddr != 0 )
                {
                    asmerror = "Too many registers";
                    scan = SCAN_ERR;
                    return;
                };

                xlataddr = 1;

                Scanasm( 0 );
            }
            else if ( scan == SCAN_REG16 )
            {
                asmerror = "Sorry, 16-bit addressing is not supported";
                scan = SCAN_ERR;
                return;
            }
            else if ( scan == SCAN_REG32 )
            {
                if ( sign == '-' )
                {
                    asmerror = "Unable to subtract register";
                    scan = SCAN_ERR;
                    return;
                };

                reg = idata;

                Scanasm( 0 );

                if ( scan == SCAN_SYMB && idata == '*' )
                {
                    Scanasm( 0 );                // Try index*scale

                    if ( scan == SCAN_ERR ) return;

                    if ( scan == SCAN_OFS )
                    {
                        asmerror = "Undefined scale is not allowed";
                        scan = SCAN_ERR;
                        return;
                    };

                    if ( scan != SCAN_ICONST && scan != SCAN_DCONST )
                    {
                        asmerror = "Syntax error";
                        scan = SCAN_ERR;
                        return;
                    };

                    if ( idata == 6 || idata == 7 || idata > 9 )
                    {
                        asmerror = "Invalid scale";
                        scan = SCAN_ERR;
                        return;
                    };

                    r[reg] += idata;

                    Scanasm( 0 );
                }
                else r[reg]++;
            }               // Simple register
            else if ( scan == SCAN_LOCAL )
            {
                r[REG_EBP]++;
                op->offset -= idata * 4;
                Scanasm( 0 );
            }
            else if ( scan == SCAN_ARG )
            {
                r[REG_EBP]++;
                op->offset += ( idata + 1 ) * 4;
                Scanasm( 0 );
            }
            else if ( scan == SCAN_ICONST || scan == SCAN_DCONST )
            {
                offset = idata;
                Scanasm( 0 );

                if ( scan == SCAN_SYMB && idata == '*' )
                {
                    Scanasm( 0 );                // Try scale*index

                    if ( scan == SCAN_ERR ) return;

                    if ( sign == '-' )
                    {
                        asmerror = "Unable to subtract register";
                        scan = SCAN_ERR;
                        return;
                    };

                    if ( scan == SCAN_REG16 )
                    {
                        asmerror = "Sorry, 16-bit addressing is not supported";
                        scan = SCAN_ERR;
                        return;
                    };

                    if ( scan != SCAN_REG32 )
                    {
                        asmerror = "Syntax error";
                        scan = SCAN_ERR;
                        return;
                    };

                    if ( offset == 6 || offset == 7 || offset > 9 )
                    {
                        asmerror = "Invalid scale";
                        scan = SCAN_ERR;
                        return;
                    };

                    r[idata] += offset;

                    Scanasm( 0 );
                }
                else
                {
                    if ( sign == '-' ) op->offset -= offset;
                    else op->offset += offset;
                };
            }
            else if ( scan == SCAN_OFS )
            {
                Scanasm( 0 );

                if ( scan == SCAN_SYMB && idata == '*' )
                {
                    asmerror = "Undefined scale is not allowed";
                    scan = SCAN_ERR;
                    return;
                }
                else
                {
                    op->anyoffset = 1;
                };
            }
            else break;                      // None of expected address elements

            if ( scan == SCAN_SYMB && idata == ']' ) break;

            sign = '?';
        };

        if ( scan == SCAN_ERR ) return;

        if ( scan != SCAN_SYMB || idata != ']' )
        {
            asmerror = "Syntax error";
            scan = SCAN_ERR;
            return;
        };

        // Process XLAT address separately.
        if ( xlataddr != 0 )               // XLAT address in form [EBX+AX]
        {
            for ( i = 0; i <= 8; i++ )       // Check which registers used
            {
                if ( i == REG_EBX ) continue;

                if ( r[i] != 0 ) break;
            };

            if ( i <= 8 || r[REG_EBX] != 1 || op->offset != 0 || op->anyoffset != 0 )
            {
                asmerror = "Invalid address";
                scan = SCAN_ERR;
                return;
            };

            op->type = MXL;
        }
        // Determine scale, index and base.
        else
        {
            j = 0;                           // Number of used registers

            for ( i = 0; i <= 8; i++ )
            {
                if ( r[i] == 0 )
                    continue;                    // Unused register

                if ( r[i] == 3 || r[i] == 5 || r[i] == 9 )
                {
                    if ( op->index >= 0 || op->base >= 0 )
                    {
                        if ( j == 0 ) asmerror = "Invalid scale";
                        else asmerror = "Too many registers";

                        scan = SCAN_ERR;
                        return;
                    };

                    op->index = op->base = i;

                    op->scale = r[i] - 1;
                }
                else if ( r[i] == 2 || r[i] == 4 || r[i] == 8 )
                {
                    if ( op->index >= 0 )
                    {
                        if ( j <= 1 ) asmerror = "Only one register may be scaled";
                        else asmerror = "Too many registers";

                        scan = SCAN_ERR;
                        return;
                    };

                    op->index = i;

                    op->scale = r[i];
                }
                else if ( r[i] == 1 )
                {
                    if ( op->base < 0 )
                        op->base = i;
                    else if ( op->index < 0 )
                    {
                        op->index = i;
                        op->scale = 1;
                    }
                    else
                    {
                        asmerror = "Too many registers";
                        scan = SCAN_ERR;
                        return;
                    };
                }
                else
                {
                    asmerror = "Invalid scale";
                    scan = SCAN_ERR;
                    return;
                };

                j++;
            };

            op->type = MRG;
        };
    }
    else
    {
        asmerror = "Unrecognized operand";
        scan = SCAN_ERR;
        return;
    };

    // In general, address modifier is allowed only with address expression which
    // is a constant, a far address or a memory expression. More precise check
    // will be done later in Assemble().
    if ( op->jmpmode != 0 && op->type != IMM && op->type != JMF && op->type != MRG )
    {
        asmerror = "Jump address modifier is not allowed";
        scan = SCAN_ERR;
        return;
    };

    Scanasm( 0 );                        // Fetch next token from input line
};

// Function assembles text into 32-bit 80x86 machine code. It supports imprecise
// operands (for example, R32 stays for any general-purpose 32-bit register).
// This allows to search for incomplete commands. Command is precise when all
// significant bytes in model.mask are 0xFF. Some commands have more than one
// decoding. By calling Assemble() with attempt=0,1... and constsize=0,1,2,3 one
// gets also alternative variants (bit 0x1 of constsize is responsible for size
// of address constant and bit 0x2 - for immediate data). However, only one
// address form is generated ([EAX*2], but not [EAX+EAX]; [EBX+EAX] but not
// [EAX+EBX]; [EAX] will not use SIB byte; no DS: prefix and so on). Returns
// number of bytes in assembled code or non-positive number in case of detected
// error. This number is the negation of the offset in the input text where the
// error encountered. Unfortunately, BC 4.52 is unable to compile the switch
// (arg) in this code when any common subexpression optimization is on. The
// next #pragma statement disables all optimizations.

//#pragma option -Od                     // No optimizations, or BC 4.52 crashes

int Assemble( char *cmd, ulong ip, t_asmmodel *model, int attempt,
              int constsize, char *errtext )
{
    int i, j, k, namelen, nameok, arg, match, datasize, addrsize, bytesize, minop, maxop;
    int rep, lock, segment, jmpsize, jmpmode, longjump;
    int hasrm, hassib, dispsize, immsize;
    int anydisp, anyimm, anyjmp;
    long l, displacement, immediate, jmpoffset;
    char name[32], *nameend;
    char tcode[MAXCMDSIZE], tmask[MAXCMDSIZE];
    t_asmoperand aop[3], *op;            // Up to 3 operands allowed
    const t_cmddata *pd;

    if ( model != NULL ) model->length = 0;

    if ( cmd == NULL || model == NULL || errtext == NULL )
    {
        if ( errtext != NULL ) strcpy( errtext, "Internal OLLYDBG error" );

        return 0;
    };                       // Error in parameters

    asmcmd = cmd;

    rep = lock = 0;

    errtext[0] = '\0';

    Scanasm( SA_NAME );

    if ( scan == SCAN_EOL )              // End of line, nothing to assemble
        return 0;

    while ( 1 )                          // Fetch all REPxx and LOCK prefixes
    {
        if ( scan == SCAN_REP || scan == SCAN_REPE || scan == SCAN_REPNE )
        {
            if ( rep != 0 )
            {
                strcpy( errtext, "Duplicated REP prefix" );
                goto error;
            };

            rep = scan;
        }
        else if ( scan == SCAN_LOCK )
        {
            if ( lock != 0 )
            {
                strcpy( errtext, "Duplicated LOCK prefix" );
                goto error;
            };

            lock = scan;
        }
        else break;                        // No more prefixes

        Scanasm( SA_NAME );
    };

    if ( scan != SCAN_NAME || idata > 16 )
    {
        strcpy( errtext, "Command mnemonic expected" );
        goto error;
    };

    nameend = asmcmd;

    strupr( sdata );

    // Prepare full mnemonic (including repeat prefix, if any).
    if ( rep == SCAN_REP ) sprintf( name, "REP %s", sdata );
    else if ( rep == SCAN_REPE ) sprintf( name, "REPE %s", sdata );
    else if ( rep == SCAN_REPNE ) sprintf( name, "REPNE %s", sdata );
    else strcpy( name, sdata );

    Scanasm( 0 );

    // Parse command operands (up to 3). Note: jump address is always the first
    // (and only) operand in actual command set.
    for ( i = 0; i < 3; i++ )
    {
        aop[i].type = NNN;                 // No operand
        aop[i].size = 0;                   // Undefined size
        aop[i].index = -1;                 // No index
        aop[i].scale = 0;                  // No scale
        aop[i].base = -1;                  // No base
        aop[i].offset = 0;                 // No offset
        aop[i].anyoffset = 0;              // No offset
        aop[i].segment = SEG_UNDEF;        // No segment
        aop[i].jmpmode = 0;
    };               // No jump size modifier

    Parseasmoperand( aop + 0 );

    jmpmode = aop[0].jmpmode;

    if ( jmpmode != 0 ) jmpmode |= 0x80;

    if ( scan == SCAN_SYMB && idata == ',' )
    {
        Scanasm( 0 );
        Parseasmoperand( aop + 1 );

        if ( scan == SCAN_SYMB && idata == ',' )
        {
            Scanasm( 0 );
            Parseasmoperand( aop + 2 );
        };
    };

    if ( scan == SCAN_ERR )
    {
        strcpy( errtext, asmerror );
        goto error;
    };

    if ( scan != SCAN_EOL )
    {
        strcpy( errtext, "Extra input after operand" );
        goto error;
    };

    // If jump size is not specified, function tries to use short jump. If
    // attempt fails, it retries with long form.
    longjump = 0;                        // Try short jump on the first pass

retrylongjump:
    nameok = 0;

    // Some commands allow different number of operands. Variables minop and
    // maxop accumulate their minimal and maximal counts. The numbers are not
    // used in assembly process but allow for better error diagnostics.
    minop = 3;

    maxop = 0;

    // Main assembly loop: try to find the command which matches all operands,
    // but do not process operands yet.
    namelen = strlen( name );

    for ( pd = cmddata; pd->mask != 0; pd++ )
    {
        if ( pd->name[0] == '&' )          // Mnemonic depends on operand size
        {
            j = 1;
            datasize = 2;
            addrsize = 4;

            while ( 1 )                      // Try all mnemonics (separated by ':')
            {
                for ( i = 0; pd->name[j] != '\0' && pd->name[j] != ':'; j++ )
                {
                    if ( pd->name[j] == '*' )
                    {
                        if ( name[i] == 'W' )
                        {
                            datasize = 2;
                            i++;
                        }
                        else if ( name[i] == 'D' )
                        {
                            datasize = 4;
                            i++;
                        }
                        else if ( sizesens == 0 ) datasize = 2;
                        else datasize = 4;
                    }
                    else if ( pd->name[j] == name[i] ) i++;
                    else break;
                };

                if ( name[i] == '\0' && ( pd->name[j] == '\0' || pd->name[j] == ':' ) )
                    break;                       // Bingo!

                while ( pd->name[j] != '\0' && pd->name[j] != ':' )
                    j++;

                if ( pd->name[j] == ':' )
                {
                    j++;
                    datasize = 4;
                }           // Retry with 32-bit mnenonic
                else
                {
                    i = 0;
                    break;                  // Comparison failed
                };
            };

            if ( i == 0 ) continue;
        }
        else if ( pd->name[0] == '$' )     // Mnemonic depends on address size
        {
            j = 1;
            datasize = 0;
            addrsize = 2;

            while ( 1 )                      // Try all mnemonics (separated by ':')
            {
                for ( i = 0; pd->name[j] != '\0' && pd->name[j] != ':'; j++ )
                {
                    if ( pd->name[j] == '*' )
                    {
                        if ( name[i] == 'W' )
                        {
                            addrsize = 2;
                            i++;
                        }
                        else if ( name[i] == 'D' )
                        {
                            addrsize = 4;
                            i++;
                        }
                        else if ( sizesens == 0 ) addrsize = 2;
                        else addrsize = 4;
                    }
                    else if ( pd->name[j] == name[i] ) i++;
                    else break;
                };

                if ( name[i] == '\0' && ( pd->name[j] == '\0' || pd->name[j] == ':' ) )
                    break;                       // Bingo!

                while ( pd->name[j] != '\0' && pd->name[j] != ':' )
                    j++;

                if ( pd->name[j] == ':' )
                {
                    j++;
                    addrsize = 4;
                }           // Retry with 32-bit mnenonic
                else
                {
                    i = 0;
                    break;                  // Comparison failed
                };
            };

            if ( i == 0 ) continue;
        }
        else                               // Compare with all synonimes
        {
            j = k = 0;
            datasize = 0;                    // Default settings
            addrsize = 4;

            while ( 1 )
            {
                while ( pd->name[j] != ',' && pd->name[j] != '\0' ) j++;

                if ( j - k == namelen && strnicmp( name, pd->name + k, namelen ) == 0 ) break;

                k = j + 1;

                if ( pd->name[j] == '\0' ) break;

                j = k;
            };

            if ( k > j ) continue;
        };

        // For error diagnostics it is important to know whether mnemonic exists.
        nameok++;

        if ( pd->arg1 == NNN || pd->arg1 >= PSEUDOOP )
            minop = 0;
        else if ( pd->arg2 == NNN || pd->arg2 >= PSEUDOOP )
        {
            if ( minop > 1 ) minop = 1;

            if ( maxop < 1 ) maxop = 1;
        }
        else if ( pd->arg3 == NNN || pd->arg3 >= PSEUDOOP )
        {
            if ( minop > 2 ) minop = 2;

            if ( maxop < 2 ) maxop = 2;
        }
        else
            maxop = 3;

        // Determine default and allowed operand size(s).
        if ( pd->bits == FF ) datasize = 2; // Forced 16-bit size

        if ( pd->bits == WW || pd->bits == WS || pd->bits == W3 || pd->bits == WP )
            bytesize = 1;                    // 1-byte size allowed
        else
            bytesize = 0;                    // Word/dword size only

        // Check whether command operands match specified. If so, variable match
        // remains zero, otherwise it contains kind of mismatch. This allows for
        // better error diagnostics.
        match = 0;

        for ( j = 0; j < 3; j++ )          // Up to 3 operands
        {
            op = aop + j;

            if ( j == 0 ) arg = pd->arg1;
            else if ( j == 1 ) arg = pd->arg2;
            else arg = pd->arg3;

            if ( arg == NNN || arg >= PSEUDOOP )
            {
                if ( op->type != NNN )         // No more arguments
                    match |= MA_NOP;

                break;
            };

            if ( op->type == NNN )
            {
                match |= MA_NOP;
                break;
            };       // No corresponding operand

            switch ( arg )
            {
            case REG:                      // Integer register in Reg field
            case RCM:                      // Integer register in command byte
            case RAC:                      // Accumulator (AL/AX/EAX, implicit)
                if ( op->type != REG ) match |= MA_TYP;

                if ( arg == RAC && op->index != REG_EAX && op->index != 8 ) match |= MA_TYP;

                if ( bytesize == 0 && op->size == 1 ) match |= MA_SIZ;

                if ( datasize == 0 ) datasize = op->size;

                if ( datasize != op->size ) match |= MA_DIF;

                break;

            case RG4:                      // Integer 4-byte register in Reg field
                if ( op->type != REG ) match |= MA_TYP;

                if ( op->size != 4 ) match |= MA_SIZ;

                if ( datasize == 0 ) datasize = op->size;

                if ( datasize != op->size ) match |= MA_DIF;

                break;

            case RAX:                      // AX (2-byte, implicit)
                if ( op->type != REG || ( op->index != REG_EAX && op->index != 8 ) )
                    match |= MA_TYP;

                if ( op->size != 2 ) match |= MA_SIZ;

                if ( datasize == 0 ) datasize = op->size;

                if ( datasize != op->size ) match |= MA_DIF;

                break;

            case RDX:                      // DX (16-bit implicit port address)
                if ( op->type != REG || ( op->index != REG_EDX && op->index != 8 ) )
                    match |= MA_TYP;

                if ( op->size != 2 ) match |= MA_SIZ;

                break;

            case RCL:                      // Implicit CL register (for shifts)
                if ( op->type != REG || ( op->index != REG_ECX && op->index != 8 ) )
                    match |= MA_TYP;

                if ( op->size != 1 ) match |= MA_SIZ;

                break;

            case RS0:                      // Top of FPU stack (ST(0))
                if ( op->type != RST || ( op->index != 0 && op->index != 8 ) )
                    match |= MA_TYP;

                break;

            case RST:                      // FPU register (ST(i)) in command byte
                if ( op->type != RST ) match |= MA_TYP;

                break;

            case RMX:                      // MMX register MMx
            case R3D:                      // 3DNow! register MMx
                if ( op->type != RMX ) match |= MA_TYP;

                break;

            case MRG:                      // Memory/register in ModRM byte
                if ( op->type != MRG && op->type != REG ) match |= MA_TYP;

                if ( bytesize == 0 && op->size == 1 ) match |= MA_SIZ;

                if ( datasize == 0 ) datasize = op->size;

                if ( op->size != 0 && op->size != datasize ) match |= MA_DIF;

                break;

            case MR1:                      // 1-byte memory/register in ModRM byte
                if ( op->type != MRG && op->type != REG ) match |= MA_TYP;

                if ( op->size != 0 && op->size != 1 ) match |= MA_SIZ;

                break;

            case MR2:                      // 2-byte memory/register in ModRM byte
                if ( op->type != MRG && op->type != REG ) match |= MA_TYP;

                if ( op->size != 0 && op->size != 2 ) match |= MA_SIZ;

                break;

            case MR4:                      // 4-byte memory/register in ModRM byte
                if ( op->type != MRG && op->type != REG ) match |= MA_TYP;

                if ( op->size != 0 && op->size != 4 ) match |= MA_SIZ;

                break;

            case RR4:                      // 4-byte memory/register (register only)
                if ( op->type != REG ) match |= MA_TYP;

                if ( op->size != 0 && op->size != 4 ) match |= MA_SIZ;

                break;

            case MRJ:                      // Memory/reg in ModRM as JUMP target
                if ( op->type != MRG && op->type != REG ) match |= MA_TYP;

                if ( op->size != 0 && op->size != 4 ) match |= MA_SIZ;

                if ( ( jmpmode & 0x09 ) != 0 ) match |= MA_JMP;

                jmpmode &= 0x7F;
                break;

            case MR8:                      // 8-byte memory/MMX register in ModRM
            case MRD:                      // 8-byte memory/3DNow! register in ModRM
                if ( op->type != MRG && op->type != RMX ) match |= MA_TYP;

                if ( op->size != 0 && op->size != 8 ) match |= MA_SIZ;

                break;

            case RR8:                      // 8-byte MMX register only in ModRM
            case RRD:                      // 8-byte memory/3DNow! (register only)
                if ( op->type != RMX ) match |= MA_TYP;

                if ( op->size != 0 && op->size != 8 ) match |= MA_SIZ;

                break;

            case MMA:                      // Memory address in ModRM byte for LEA
                if ( op->type != MRG ) match |= MA_TYP;

                break;

            case MML:                      // Memory in ModRM byte (for LES)
                if ( op->type != MRG ) match |= MA_TYP;

                if ( op->size != 0 && op->size != 6 ) match |= MA_SIZ;

                if ( datasize == 0 ) datasize = 4;
                else if ( datasize != 4 ) match |= MA_DIF;

                break;

            case MMS:                      // Memory in ModRM byte (as SEG:OFFS)
                if ( op->type != MRG ) match |= MA_TYP;

                if ( op->size != 0 && op->size != 6 ) match |= MA_SIZ;

                if ( ( jmpmode & 0x07 ) != 0 ) match |= MA_JMP;

                jmpmode &= 0x7F;
                break;

            case MM6:                      // Memory in ModRm (6-byte descriptor)
                if ( op->type != MRG ) match |= MA_TYP;

                if ( op->size != 0 && op->size != 6 ) match |= MA_SIZ;

                break;

            case MMB:                      // Two adjacent memory locations (BOUND)
                if ( op->type != MRG ) match |= MA_TYP;

                k = op->size;

                if ( ideal == 0 && k > 1 ) k /= 2;

                if ( k != 0 && k != datasize ) match |= MA_DIF;

                break;

            case MD2:                      // Memory in ModRM byte (16-bit integer)
            case MB2:                      // Memory in ModRM byte (16-bit binary)
                if ( op->type != MRG ) match |= MA_TYP;

                if ( op->size != 0 && op->size != 2 ) match |= MA_SIZ;

                break;

            case MD4:                      // Memory in ModRM byte (32-bit integer)
            case MF4:                      // Memory in ModRM byte (32-bit float)
                if ( op->type != MRG ) match |= MA_TYP;

                if ( op->size != 0 && op->size != 4 ) match |= MA_SIZ;

                break;

            case MD8:                      // Memory in ModRM byte (64-bit integer)
            case MF8:                      // Memory in ModRM byte (64-bit float)
                if ( op->type != MRG ) match |= MA_TYP;

                if ( op->size != 0 && op->size != 8 ) match |= MA_SIZ;

                break;

            case MDA:                      // Memory in ModRM byte (80-bit BCD)
            case MFA:                      // Memory in ModRM byte (80-bit float)
                if ( op->type != MRG ) match |= MA_TYP;

                if ( op->size != 0 && op->size != 10 ) match |= MA_SIZ;

                break;

            case MFE:                      // Memory in ModRM byte (FPU environment)
            case MFS:                      // Memory in ModRM byte (FPU state)
            case MFX:                      // Memory in ModRM byte (ext. FPU state)
                if ( op->type != MRG ) match |= MA_TYP;

                if ( op->size != 0 ) match |= MA_SIZ;

                break;

            case MSO:                      // Source in string operands ([ESI])
                if ( op->type != MRG || op->base != REG_ESI ||
                        op->index != -1 || op->offset != 0 || op->anyoffset != 0 ) match |= MA_TYP;

                if ( datasize == 0 ) datasize = op->size;

                if ( op->size != 0 && op->size != datasize ) match |= MA_DIF;

                break;

            case MDE:                      // Destination in string operands ([EDI])
                if ( op->type != MRG || op->base != REG_EDI ||
                        op->index != -1 || op->offset != 0 || op->anyoffset != 0 ) match |= MA_TYP;

                if ( op->segment != SEG_UNDEF && op->segment != SEG_ES ) match |= MA_SEG;

                if ( datasize == 0 ) datasize = op->size;

                if ( op->size != 0 && op->size != datasize ) match |= MA_DIF;

                break;

            case MXL:                      // XLAT operand ([EBX+AL])
                if ( op->type != MXL ) match |= MA_TYP;

                break;

            case IMM:                      // Immediate data (8 or 16/32)
            case IMU:                      // Immediate unsigned data (8 or 16/32)
                if ( op->type != IMM ) match |= MA_TYP;

                break;

            case VXD:                      // VxD service (32-bit only)
                if ( op->type != IMM ) match |= MA_TYP;

                if ( datasize == 0 ) datasize = 4;

                if ( datasize != 4 ) match |= MA_SIZ;

                break;

            case JMF:                      // Immediate absolute far jump/call addr
                if ( op->type != JMF ) match |= MA_TYP;

                if ( ( jmpmode & 0x05 ) != 0 ) match |= MA_JMP;

                jmpmode &= 0x7F;
                break;

            case JOB:                      // Immediate byte offset (for jumps)
                if ( op->type != IMM || longjump ) match |= MA_TYP;

                if ( ( jmpmode & 0x0A ) != 0 ) match |= MA_JMP;

                jmpmode &= 0x7F;
                break;

            case JOW:                      // Immediate full offset (for jumps)
                if ( op->type != IMM ) match |= MA_TYP;

                if ( ( jmpmode & 0x09 ) != 0 ) match |= MA_JMP;

                jmpmode &= 0x7F;
                break;

            case IMA:                      // Immediate absolute near data address
                if ( op->type != MRG || op->base >= 0 || op->index >= 0 ) match |= MA_TYP;

                break;

            case IMX:                      // Immediate sign-extendable byte
                if ( op->type != IMM ) match |= MA_TYP;

                if ( op->offset < -128 || op->offset > 127 ) match |= MA_RNG;

                break;

            case C01:                      // Implicit constant 1 (for shifts)
                if ( op->type != IMM || ( op->offset != 1 && op->anyoffset == 0 ) )
                    match |= MA_TYP;

                break;

            case IMS:                      // Immediate byte (for shifts)
            case IM1:                      // Immediate byte
                if ( op->type != IMM ) match |= MA_TYP;

                if ( op->offset < -128 || op->offset > 255 ) match |= MA_RNG;

                break;

            case IM2:                      // Immediate word (ENTER/RET)
                if ( op->type != IMM ) match |= MA_TYP;

                if ( op->offset < 0 || op->offset > 65535 ) match |= MA_RNG;

                break;

            case SGM:                      // Segment register in ModRM byte
                if ( op->type != SGM ) match |= MA_TYP;

                if ( datasize == 0 ) datasize = 2;

                if ( datasize != 2 ) match |= MA_DIF;

                break;

            case SCM:                      // Segment register in command byte
                if ( op->type != SGM ) match |= MA_TYP;

                break;

            case CRX:                      // Control register CRx
            case DRX:                      // Debug register DRx
                if ( op->type != arg ) match |= MA_TYP;

                if ( datasize == 0 ) datasize = 4;

                if ( datasize != 4 ) match |= MA_DIF;

                break;

            case PRN:                      // Near return address (pseudooperand)
            case PRF:                      // Far return address (pseudooperand)
            case PAC:                      // Accumulator (AL/AX/EAX, pseudooperand)
            case PAH:                      // AH (in LAHF/SAHF, pseudooperand)
            case PFL:                      // Lower byte of flags (pseudooperand)
            case PS0:                      // Top of FPU stack (pseudooperand)
            case PS1:                      // ST(1) (pseudooperand)
            case PCX:                      // CX/ECX (pseudooperand)
            case PDI:                      // EDI (pseudooperand in MMX extentions)
                break;

            default:                       // Undefined type of operand
                strcpy( errtext, "Internal Assembler error" );
                goto error;
            };                               // End of switch (arg)

            if ( ( jmpmode & 0x80 ) != 0 ) match |= MA_JMP;

            if ( match != 0 ) break;         // Some of the operands doesn't match
        };                                 // End of operand matching loop

        if ( match == 0 )                  // Exact match found
        {
            if ( attempt > 0 )
            {
                --attempt;
                nameok = 0;
            }         // Well, try to find yet another match
            else break;
        };
    };                                   // End of command search loop

    // Check whether some error was detected. If several errors were found
    // similtaneously, report one (roughly in order of significance).
    if ( nameok == 0 )                   // Mnemonic unavailable
    {
        strcpy( errtext, "Unrecognized command" );
        asmcmd = nameend;
        goto error;
    };

    if ( match != 0 )                    // Command not found
    {
        if ( minop > 0 && aop[minop - 1].type == NNN )
            strcpy( errtext, "Too few operands" );
        else if ( maxop < 3 && aop[maxop].type != NNN )
            strcpy( errtext, "Too many operands" );
        else if ( nameok > 1 )             // More that 1 command
            strcpy( errtext, "Command does not support given operands" );
        else if ( match & MA_JMP )
            strcpy( errtext, "Invalid jump size modifier" );
        else if ( match & MA_NOP )
            strcpy( errtext, "Wrong number of operands" );
        else if ( match & MA_TYP )
            strcpy( errtext, "Command does not support given operands" );
        else if ( match & MA_NOS )
            strcpy( errtext, "Please specify operand size" );
        else if ( match & MA_SIZ )
            strcpy( errtext, "Bad operand size" );
        else if ( match & MA_DIF )
            strcpy( errtext, "Different size of operands" );
        else if ( match & MA_SEG )
            strcpy( errtext, "Invalid segment register" );
        else if ( match & MA_RNG )
            strcpy( errtext, "Constant out of expected range" );
        else
            strcpy( errtext, "Erroneous command" );

        goto error;
    };

    // Exact match found. Now construct the code.
    hasrm = 0;                           // Whether command has ModR/M byte

    hassib = 0;                          // Whether command has SIB byte

    dispsize = 0;                        // Size of displacement (if any)

    immsize = 0;                         // Size of immediate data (if any)

    segment = SEG_UNDEF;                 // Necessary segment prefix

    jmpsize = 0;                         // No relative jumps

    memset( tcode, 0, sizeof( tcode ) );

    *( ulong * )tcode = pd->code & pd->mask;

    memset( tmask, 0, sizeof( tmask ) );

    *( ulong * )tmask = pd->mask;

    i = pd->len - 1;                     // Last byte of command itself

    if ( rep ) i++;                      // REPxx prefixes count as extra byte

    // In some cases at least one operand must have explicit size declaration (as
    // in MOV [EAX],1). This preliminary check does not include all cases.
    if ( pd->bits == WW || pd->bits == WS || pd->bits == WP )
    {
        if ( datasize == 0 )
        {
            strcpy( errtext, "Please specify operand size" );
            goto error;
        }
        else if ( datasize > 1 )
            tcode[i] |= 0x01;                // WORD or DWORD size of operands

        tmask[i] |= 0x01;
    }
    else if ( pd->bits == W3 )
    {
        if ( datasize == 0 )
        {
            strcpy( errtext, "Please specify operand size" );
            goto error;
        }
        else if ( datasize > 1 )
            tcode[i] |= 0x08;                // WORD or DWORD size of operands

        tmask[i] |= 0x08;
    };

    // Present suffix of 3DNow! command as immediate byte operand.
    if ( ( pd->type & C_TYPEMASK ) == C_NOW )
    {
        immsize = 1;
        immediate = ( pd->code >> 16 ) & 0xFF;
    };

    // Process operands again, this time constructing the code.
    anydisp = anyimm = anyjmp = 0;

    for ( j = 0; j < 3; j++ )            // Up to 3 operands
    {
        op = aop + j;

        if ( j == 0 ) arg = pd->arg1;
        else if ( j == 1 ) arg = pd->arg2;
        else arg = pd->arg3;

        if ( arg == NNN ) break;           // All operands processed

        switch ( arg )
        {
        case REG:                        // Integer register in Reg field
        case RG4:                        // Integer 4-byte register in Reg field
        case RMX:                        // MMX register MMx
        case R3D:                        // 3DNow! register MMx
        case CRX:                        // Control register CRx
        case DRX:                        // Debug register DRx
            hasrm = 1;

            if ( op->index < 8 )
            {
                tcode[i + 1] |= ( char )( op->index << 3 );
                tmask[i + 1] |= 0x38;
            };

            break;

        case RCM:                        // Integer register in command byte
        case RST:                        // FPU register (ST(i)) in command byte
            if ( op->index < 8 )
            {
                tcode[i] |= ( char )op->index;
                tmask[i] |= 0x07;
            };

            break;

        case RAC:                        // Accumulator (AL/AX/EAX, implicit)
        case RAX:                        // AX (2-byte, implicit)
        case RDX:                        // DX (16-bit implicit port address)
        case RCL:                        // Implicit CL register (for shifts)
        case RS0:                        // Top of FPU stack (ST(0))
        case MDE:                        // Destination in string op's ([EDI])
        case C01:                        // Implicit constant 1 (for shifts)
            break;                         // Simply skip implicit operands

        case MSO:                        // Source in string op's ([ESI])
        case MXL:                        // XLAT operand ([EBX+AL])
            if ( op->segment != SEG_UNDEF && op->segment != SEG_DS )
                segment = op->segment;

            break;

        case MRG:                        // Memory/register in ModRM byte
        case MRJ:                        // Memory/reg in ModRM as JUMP target
        case MR1:                        // 1-byte memory/register in ModRM byte
        case MR2:                        // 2-byte memory/register in ModRM byte
        case MR4:                        // 4-byte memory/register in ModRM byte
        case RR4:                        // 4-byte memory/register (register only)
        case MR8:                        // 8-byte memory/MMX register in ModRM
        case RR8:                        // 8-byte MMX register only in ModRM
        case MRD:                        // 8-byte memory/3DNow! register in ModRM
        case RRD:                        // 8-byte memory/3DNow! (register only)
            hasrm = 1;

            if ( op->type != MRG )         // Register in ModRM byte
            {
                tcode[i + 1] |= 0xC0;
                tmask[i + 1] |= 0xC0;

                if ( op->index < 8 )
                {
                    tcode[i + 1] |= ( char )op->index;
                    tmask[i + 1] |= 0x07;
                };

                break;
            };                             // Note: NO BREAK, continue with address

        case MMA:                        // Memory address in ModRM byte for LEA
        case MML:                        // Memory in ModRM byte (for LES)
        case MMS:                        // Memory in ModRM byte (as SEG:OFFS)
        case MM6:                        // Memory in ModRm (6-byte descriptor)
        case MMB:                        // Two adjacent memory locations (BOUND)
        case MD2:                        // Memory in ModRM byte (16-bit integer)
        case MB2:                        // Memory in ModRM byte (16-bit binary)
        case MD4:                        // Memory in ModRM byte (32-bit integer)
        case MD8:                        // Memory in ModRM byte (64-bit integer)
        case MDA:                        // Memory in ModRM byte (80-bit BCD)
        case MF4:                        // Memory in ModRM byte (32-bit float)
        case MF8:                        // Memory in ModRM byte (64-bit float)
        case MFA:                        // Memory in ModRM byte (80-bit float)
        case MFE:                        // Memory in ModRM byte (FPU environment)
        case MFS:                        // Memory in ModRM byte (FPU state)
        case MFX:                        // Memory in ModRM byte (ext. FPU state)
            hasrm = 1;
            displacement = op->offset;
            anydisp = op->anyoffset;

            if ( op->base < 0 && op->index < 0 )
            {
                dispsize = 4;                // Special case of immediate address

                if ( op->segment != SEG_UNDEF && op->segment != SEG_DS )
                    segment = op->segment;

                tcode[i + 1] |= 0x05;
                tmask[i + 1] |= 0xC7;
            }
            else if ( op->index < 0 && op->base != REG_ESP )
            {
                tmask[i + 1] |= 0xC0;        // SIB byte unnecessary

                if ( op->offset == 0 && op->anyoffset == 0 && op->base != REG_EBP )
                    ;                          // [EBP] always requires offset
                else if ( ( constsize & 1 ) != 0 &&
                          ( ( op->offset >= -128 && op->offset < 128 ) || op->anyoffset != 0 )
                        )
                {
                    tcode[i + 1] |= 0x40;      // Disp8
                    dispsize = 1;
                }
                else
                {
                    tcode[i + 1] |= 0x80;      // Disp32
                    dispsize = 4;
                };

                if ( op->base < 8 )
                {
                    if ( op->segment != SEG_UNDEF && op->segment != addr32[op->base].defseg )
                        segment = op->segment;

                    tcode[i + 1] |=
                        ( char )op->base;        // Note that case [ESP] has base<0.
                    tmask[i + 1] |= 0x07;
                }
                else segment = op->segment;
            }
            else                           // SIB byte necessary
            {
                hassib = 1;

                if ( op->base == REG_EBP &&  // EBP as base requires offset, optimize
                        op->index >= 0 && op->scale == 1 && op->offset == 0 && op->anyoffset == 0 )
                {
                    op->base = op->index;
                    op->index = REG_EBP;
                };

                if ( op->index == REG_ESP && // ESP cannot be an index, reorder
                        op->scale <= 1 )
                {
                    op->index = op->base;
                    op->base = REG_ESP;
                    op->scale = 1;
                };

                if ( op->base < 0 &&         // No base means 4-byte offset, optimize
                        op->index >= 0 && op->scale == 2 &&
                        op->offset >= -128 && op->offset < 128 && op->anyoffset == 0 )
                {
                    op->base = op->index;
                    op->scale = 1;
                };

                if ( op->index == REG_ESP )  // Reordering was unsuccessfull
                {
                    strcpy( errtext, "Invalid indexing mode" );
                    goto error;
                };

                if ( op->base < 0 )
                {
                    tcode[i + 1] |= 0x04;
                    dispsize = 4;
                }
                else if ( op->offset == 0 && op->anyoffset == 0 && op->base != REG_EBP )
                    tcode[i + 1] |= 0x04;      // No displacement
                else if ( ( constsize & 1 ) != 0 &&
                          ( ( op->offset >= -128 && op->offset < 128 ) || op->anyoffset != 0 )
                        )
                {
                    tcode[i + 1] |= 0x44;      // Disp8
                    dispsize = 1;
                }
                else
                {
                    tcode[i + 1] |= 0x84;      // Disp32
                    dispsize = 4;
                };

                tmask[i + 1] |= 0xC7;        // ModRM completed, proceed with SIB

                if ( op->scale == 2 ) tcode[i + 2] |= 0x40;
                else if ( op->scale == 4 ) tcode[i + 2] |= 0x80;
                else if ( op->scale == 8 ) tcode[i + 2] |= 0xC0;

                tmask[i + 2] |= 0xC0;

                if ( op->index < 8 )
                {
                    if ( op->index < 0 ) op->index = 0x04;

                    tcode[i + 2] |= ( char )( op->index << 3 );
                    tmask[i + 2] |= 0x38;
                };

                if ( op->base < 8 )
                {
                    if ( op->base < 0 ) op->base = 0x05;

                    if ( op->segment != SEG_UNDEF && op->segment != addr32[op->base].defseg )
                        segment = op->segment;

                    tcode[i + 2] |= ( char )op->base;
                    tmask[i + 2] |= 0x07;
                }
                else segment = op->segment;
            };

            break;

        case IMM:                        // Immediate data (8 or 16/32)
        case IMU:                        // Immediate unsigned data (8 or 16/32)
        case VXD:                        // VxD service (32-bit only)
            if ( datasize == 0 && pd->arg2 == NNN && ( pd->bits == SS || pd->bits == WS ) )
                datasize = 4;

            if ( datasize == 0 )
            {
                strcpy( errtext, "Please specify operand size" );
                goto error;
            };

            immediate = op->offset;

            anyimm = op->anyoffset;

            if ( pd->bits == SS || pd->bits == WS )
            {
                if ( datasize > 1 && ( constsize & 2 ) != 0 &&
                        ( ( immediate >= -128 && immediate < 128 ) || op->anyoffset != 0 ) )
                {
                    immsize = 1;
                    tcode[i] |= 0x02;
                }
                else immsize = datasize;

                tmask[i] |= 0x02;
            }
            else immsize = datasize;

            break;

        case IMX:                        // Immediate sign-extendable byte
        case IMS:                        // Immediate byte (for shifts)
        case IM1:                        // Immediate byte
            if ( immsize == 2 )            // To accomodate ENTER instruction
                immediate = ( immediate & 0xFFFF ) | ( op->offset << 16 );
            else immediate = op->offset;

            anyimm |= op->anyoffset;
            immsize++;
            break;

        case IM2:                        // Immediate word (ENTER/RET)
            immediate = op->offset;
            anyimm = op->anyoffset;
            immsize = 2;
            break;

        case IMA:                        // Immediate absolute near data address
            if ( op->segment != SEG_UNDEF && op->segment != SEG_DS )
                segment = op->segment;

            displacement = op->offset;
            anydisp = op->anyoffset;
            dispsize = 4;
            break;

        case JOB:                        // Immediate byte offset (for jumps)
            jmpoffset = op->offset;
            anyjmp = op->anyoffset;
            jmpsize = 1;
            break;

        case JOW:                        // Immediate full offset (for jumps)
            jmpoffset = op->offset;
            anyjmp = op->anyoffset;
            jmpsize = 4;
            break;

        case JMF:                        // Immediate absolute far jump/call addr
            displacement = op->offset;
            anydisp = op->anyoffset;
            dispsize = 4;
            immediate = op->segment;
            anyimm = op->anyoffset;
            immsize = 2;
            break;

        case SGM:                        // Segment register in ModRM byte
            hasrm = 1;

            if ( op->index < 6 )
            {
                tcode[i + 1] |= ( char )( op->index << 3 );
                tmask[i + 1] |= 0x38;
            };

            break;

        case SCM:                        // Segment register in command byte
            if ( op->index == SEG_FS || op->index == SEG_GS )
            {
                tcode[0] = 0x0F;
                tmask[0] = 0xFF;
                i = 1;

                if ( strcmp( name, "PUSH" ) == 0 )
                    tcode[i] = ( char )( ( op->index << 3 ) | 0x80 );
                else
                    tcode[i] = ( char )( ( op->index << 3 ) | 0x81 );

                tmask[i] = 0xFF;
            }
            else if ( op->index < 6 )
            {
                if ( op->index == SEG_CS && strcmp( name, "POP" ) == 0 )
                {
                    strcpy( errtext, "Unable to POP CS" );
                    goto error;
                };

                tcode[i] = ( char )( ( tcode[i] & 0xC7 ) | ( op->index << 3 ) );
            }
            else
            {
                tcode[i] &= 0xC7;
                tmask[i] &= 0xC7;
            };

            break;

        case PRN:                        // Near return address (pseudooperand)
        case PRF:                        // Far return address (pseudooperand)
        case PAC:                        // Accumulator (AL/AX/EAX, pseudooperand)
        case PAH:                        // AH (in LAHF/SAHF, pseudooperand)
        case PFL:                        // Lower byte of flags (pseudooperand)
        case PS0:                        // Top of FPU stack (pseudooperand)
        case PS1:                        // ST(1) (pseudooperand)
        case PCX:                        // CX/ECX (pseudooperand)
        case PDI:                        // EDI (pseudooperand in MMX extentions)
            break;                         // Simply skip preudooperands

        default:                         // Undefined type of operand
            strcpy( errtext, "Internal Assembler error" );
            goto error;
        };
    };

    // Gather parts of command together in the complete command.
    j = 0;

    if ( lock != 0 )                     // Lock prefix specified
    {
        model->code[j] = 0xF0;
        model->mask[j] = 0xFF;
        j++;
    };

    if ( datasize == 2 && pd->bits != FF ) // Data size prefix necessary
    {
        model->code[j] = 0x66;
        model->mask[j] = 0xFF;
        j++;
    };

    if ( addrsize == 2 )                 // Address size prefix necessary
    {
        model->code[j] = 0x67;
        model->mask[j] = 0xFF;
        j++;
    };

    if ( segment != SEG_UNDEF )          // Segment prefix necessary
    {
        if ( segment == SEG_ES ) model->code[j] = 0x26;
        else if ( segment == SEG_CS ) model->code[j] = 0x2E;
        else if ( segment == SEG_SS ) model->code[j] = 0x36;
        else if ( segment == SEG_DS ) model->code[j] = 0x3E;
        else if ( segment == SEG_FS ) model->code[j] = 0x64;
        else if ( segment == SEG_GS ) model->code[j] = 0x65;
        else
        {
            strcpy( errtext, "Internal Assembler error" );
            goto error;
        };

        model->mask[j] = 0xFF;

        j++;
    };

    if ( dispsize > 0 )
    {
        memcpy( tcode + i + 1 + hasrm + hassib, &displacement, dispsize );

        if ( anydisp == 0 ) memset( tmask + i + 1 + hasrm + hassib, 0xFF, dispsize );
    };

    if ( immsize > 0 )
    {
        if ( immsize == 1 ) l = 0xFFFFFF00L;
        else if ( immsize == 2 ) l = 0xFFFF0000L;
        else l = 0L;

        if ( ( immediate & l ) != 0 && ( immediate & l ) != l )
        {
            strcpy( errtext, "Constant does not fit into operand" );
            goto error;
        };

        memcpy( tcode + i + 1 + hasrm + hassib + dispsize, &immediate, immsize );

        if ( anyimm == 0 ) memset( tmask + i + 1 + hasrm + hassib + dispsize, 0xFF, immsize );
    };

    i = i + 1 + hasrm + hassib + dispsize + immsize;

    jmpoffset = jmpoffset - ( i + j + jmpsize );

    model->jmpsize = jmpsize;

    model->jmpoffset = jmpoffset;

    model->jmppos = i + j;

    if ( jmpsize != 0 )
    {
        if ( ip != 0 )
        {
            jmpoffset = jmpoffset - ip;

            if ( jmpsize == 1 && anyjmp == 0 && ( jmpoffset < -128 || jmpoffset >= 128 ) )
            {
                if ( longjump == 0 && ( jmpmode & 0x03 ) == 0 )
                {
                    longjump = 1;
                    goto retrylongjump;
                };

                sprintf( errtext,
                         "Relative jump out of range, use %s LONG form", name );

                goto error;
            };

            memcpy( tcode + i, &jmpoffset, jmpsize );
        };

        if ( anyjmp == 0 ) memset( tmask + i, 0xFF, jmpsize );

        i += jmpsize;
    };

    memcpy( model->code + j, tcode, i );

    memcpy( model->mask + j, tmask, i );

    i += j;

    model->length = i;

    return i;                            // Positive value: length of code

error:
    model->length = 0;

    return cmd - asmcmd;                 // Negative value: position of error
};

//#pragma option -O.                     // Restore old optimization options

